﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;

namespace Sofire.DataComm.Net
{
    /// <summary>
    /// 指定支持的通讯协议。
    /// </summary>
    public enum ProtocolMode
    {
        /// <summary>
        /// 传输控制协议。
        /// </summary>
        Tcp = 6,
        /// <summary>
        /// 用户数据报协议。
        /// </summary>
        Udp = 17
    }

    /// <summary>
    /// 表示套接字的信息。
    /// </summary>
    public class SocketInfo
    {
        private bool _IsSupportsIPv6;
        /// <summary>
        /// 获取一个值，表示套接字是否支持 IPv6。
        /// </summary>
        public bool IsSupportsIPv6
        {
            get
            {
                return this._IsSupportsIPv6;
            }
        }

        private EndPoint _EndPoint;
        /// <summary>
        /// 获取套接字的网络地址。
        /// </summary>
        public EndPoint EndPoint
        {
            get
            {
                return this._EndPoint;
            }
        }

        private ProtocolMode _ProtocolMode = ProtocolMode.Tcp;
        /// <summary>
        /// 获取支持的通讯协议。
        /// </summary>
        public ProtocolMode ProtocolMode
        {
            get
            {
                return this._ProtocolMode;
            }
        }

#if !SILVERLIGHT
        /// <summary>
        /// 获取或设置一个值，该值指定之后同步 System.Net.Sockets.Socket.Receive 调用将超时的时间长度。
        /// <para>超时值（以毫秒为单位）。默认值为 0，指示超时期限无限大。指定 -1 还会指示超时期限无限大。</para>
        /// </summary>
        public int ReceiveTimeout { get; set; }

        /// <summary>
        /// 获取或设置一个值，该值指定之后同步 System.Net.Sockets.Socket.Send 调用将超时的时间长度。超时值（以毫秒为单位）。如果将该属性设置为 1 到 499 之间的值，该值将被更改为 500。默认值为 0，指示超时期限无限大。指定 -1 还会指示超时期限无限大。
        /// </summary>
        public int SendTimeout { get; set; }
#endif
        /// <summary>
        /// 指定网络 IP 和网络端口，初始化 <see cref="Sofire.DataComm.Net.SocketInfo"/> 的新实例。
        /// </summary>
        /// <param name="mode">支持的通讯协议。</param>
        /// <param name="ip">网络 IP。</param>
        /// <param name="port">网络端口。</param>
        public SocketInfo(ProtocolMode mode, string ip, int port)
            : this(mode, new IPEndPoint(NetExts.ParseAddress(ip), port)) { }

        /// <summary>
        /// 指定网络地址，初始化 <see cref="Sofire.DataComm.Net.SocketInfo"/> 的新实例。
        /// </summary>
        /// <param name="mode">支持的通讯协议。</param>
        /// <param name="endPoint">网络地址。</param>
        public SocketInfo(ProtocolMode mode, IPEndPoint endPoint)
        {
            this._EndPoint = endPoint;
            this._ProtocolMode = mode;
        }

        /// <summary>
        ///指定 <see cref="System.Net.IPAddress.Loopback"/> 和网络端口，初始化 <see cref="Sofire.DataComm.Net.SocketInfo"/> 的新实例。
        /// </summary>
        /// <param name="mode">支持的通讯协议。</param>
        /// <param name="port">网络端口。</param>
        public SocketInfo(ProtocolMode mode, int port)
            : this(mode, port, false) { }

        /// <summary>
        /// 指定 <see cref="System.Net.IPAddress.Loopback"/> 和网络端口，初始化 <see cref="Sofire.DataComm.Net.SocketInfo"/> 的新实例。
        /// </summary>
        /// <param name="mode">支持的通讯协议。</param>
        /// <param name="port">网络端口。</param>
        /// <param name="isSupportsIPv6">指示网络 IP 是否支持 IPv6。</param>
        public SocketInfo(ProtocolMode mode, int port, bool isSupportsIPv6)
            : this(mode, new IPEndPoint(IPAddress.Loopback, port))
        {
            this._IsSupportsIPv6 = isSupportsIPv6;
        }

#if !SILVERLIGHT
        
        private byte[] KeepAliveOptionValues;
        private uint _KeepAliveSeconds = 1200000U;
        /// <summary>
        /// 设置或获取以秒为单位的心跳侦测间隔。默认为 1200 秒。当为 0 时表示采用操作系统的默认心跳侦测间隔。
        /// </summary>
        public uint KeepAliveSeconds
        {
            get
            {
                return this._KeepAliveSeconds / 1000U;
            }
            set
            {
                value *= 1000U;
                if(this._KeepAliveSeconds != value)
                {
                    this._KeepAliveSeconds = value;
                    KeepAliveOptionValues = null;
                }
            }
        }

        private void SetKeepAlive(Socket scoket)
        {
            if(KeepAliveOptionValues == null)
            {
                uint dummy = 0;
                KeepAliveOptionValues = new byte[Marshal.SizeOf(dummy) * 3];

                //- 是否启用Keep-Alive
                BitConverter.GetBytes((uint)1).CopyTo(KeepAliveOptionValues, 0);
                //- 首次探测时间
                BitConverter.GetBytes(_KeepAliveSeconds).CopyTo(KeepAliveOptionValues, Marshal.SizeOf(dummy));
                //- 间隔探测时间
                BitConverter.GetBytes(_KeepAliveSeconds).CopyTo(KeepAliveOptionValues, Marshal.SizeOf(dummy) * 2);
            }
            scoket.IOControl(IOControlCode.KeepAliveValues, KeepAliveOptionValues, null);
        }
#endif
        /// <summary>
        /// 创建一个 <see cref="System.Net.Sockets.Socket"/>。
        /// </summary>
        /// <returns>返回一个 <see cref="System.Net.Sockets.Socket"/>。</returns>
        public Socket CreateSocket()
        {
            Socket socket;

            var addressFamily = (this.IsSupportsIPv6 && Socket.OSSupportsIPv6 && (this._EndPoint.AddressFamily == AddressFamily.InterNetworkV6))
                ? AddressFamily.InterNetworkV6
                : AddressFamily.InterNetwork;
            if(this._ProtocolMode == Net.ProtocolMode.Tcp)
            {
                socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
            }
            else
            {
                socket = new Socket(addressFamily, SocketType.Dgram, ProtocolType.Udp);
            }
#if !SILVERLIGHT
            if(this._KeepAliveSeconds > 0U)
            {
                this.SetKeepAlive(socket);
            }
            if(this.ReceiveTimeout != 0)
                socket.ReceiveTimeout = this.ReceiveTimeout;
            if(this.SendTimeout != 0)
                socket.SendTimeout = this.SendTimeout;
#endif
            return socket;
        }

    }

    ///// <summary>
    ///// 一个基于 TCP/IP 的可靠套接字实现。
    ///// </summary>
    //public abstract class TcpSocket : SocketBase
    //{
    //    /// <summary>
    //    /// 设置或获取一个值，表示是否支持 IPv6。
    //    /// </summary>
    //    public bool IsSupportsIPv6 { get; set; }

    //    /// <summary>
    //    /// 可靠套接字的网络地址。
    //    /// </summary>
    //    protected EndPoint _EndPoint;
    //    /// <summary>
    //    /// 获取可靠套接字的网络地址。
    //    /// </summary>
    //    public EndPoint EndPoint
    //    {
    //        get
    //        {
    //            return this._EndPoint;
    //        }
    //    }

    //    /// <summary>
    //    /// 初始化 <see cref="Sofire.DataComm.Net.TcpSocket"/> 的新实例。
    //    /// </summary>
    //    /// <param name="ip">网络 IP。</param>
    //    /// <param name="port">网络端口。</param>
    //    public TcpSocket(string ip, int port)
    //    {
    //        this._EndPoint = new IPEndPoint(NetExts.ParseAddress(ip), port);
    //    }

    //    /// <summary>
    //    /// 创建一个 <see cref="System.Net.Sockets.Socket"/>。
    //    /// </summary>
    //    /// <returns>返回一个 <see cref="System.Net.Sockets.Socket"/>。</returns>
    //    protected override Socket CreateSocket()
    //    {
    //        if(this.IsSupportsIPv6 && Socket.OSSupportsIPv6 && (this._EndPoint.AddressFamily == AddressFamily.InterNetworkV6))
    //            return new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
    //        else
    //            return new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    //    }
    //}
}
